package kvoog.arkanoid.physics;

import kvoog.arkanoid.core.ArkanoidEntityFactory;
import kvoog.arkanoid.core.KeyInputReceiver;
import kvoog.arkanoid.rendering.Transform;

import com.artemis.Aspect;
import com.artemis.ComponentMapper;
import com.artemis.Entity;
import com.artemis.annotations.Mapper;
import com.artemis.systems.IntervalEntitySystem;
import com.artemis.utils.ImmutableBag;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.badlogic.gdx.physics.box2d.World;

public class PhysicsSystem extends IntervalEntitySystem implements ContactListener {

	private static final float		BOX_STEP = 1/60f;
	private static final boolean	BOX_DEBUG_RENDERER = false;
		
	@Mapper private ComponentMapper<Physics> phm;
	@Mapper private ComponentMapper<Transform> tfm;
	@Mapper private ComponentMapper<KeyInputReceiver> kim;

	private World physicsWorld;
	private int velocityIterations = 6;
	private int positionIterations = 2;
	private Box2DDebugRenderer debugRenderer;
	private OrthographicCamera camera;
	
	@SuppressWarnings("unchecked")
	public PhysicsSystem(World physicsWorld) {
		super(Aspect.getAspectForAll(Physics.class), BOX_STEP);
		this.physicsWorld = physicsWorld;
	}
	
	@SuppressWarnings("unchecked")
	public PhysicsSystem(World physicsWorld, int velocityIterations, int positionIterations) {
		super(Aspect.getAspectForAll(Physics.class), BOX_STEP);
		this.physicsWorld = physicsWorld;
		this.velocityIterations = velocityIterations;
		this.positionIterations = positionIterations;
	}
	
	@Override
	protected void initialize() {
		super.initialize();

		physicsWorld.setContactListener(this);
		
		debugRenderer = new Box2DDebugRenderer();
		
		camera = new OrthographicCamera(8, 4.8f);
		camera.translate(4, 2.4f);
		camera.update();
	}

	@Override
	protected void inserted(Entity e) {
		// must be done here because entity is not yet created in factory method
		Physics physics = phm.get(e); 
		physics.body.setUserData(e);
	}

	@Override
	protected void removed(Entity e) {
		Physics physics = phm.get(e);
		physicsWorld.destroyBody(physics.body);
	}

	@Override
	protected void processEntities(ImmutableBag<Entity> entities) {
		physicsWorld.step(BOX_STEP, velocityIterations, positionIterations);
		
		for (int i = 0, n = entities.size(); i < n; i++) {
			Entity e = entities.get(i);
			Physics physics = phm.get(e);
			Body body = physics.body;
			
			Transform transform = tfm.getSafe(e);
			if (transform != null) {			
				transform.position = body.getPosition();
				transform.rotation = body.getAngle();
			}
			
			KeyInputReceiver kiRec = kim.getSafe(e);
			if (kiRec != null) {
				if (kiRec.keyMapping.containsKey("MOVE_LEFT")) {
					KeyInputReceiver.KeyInputData kiData = kiRec.keyMapping.get("MOVE_LEFT");
					if (kiData.pressed)
						body.applyLinearImpulse(new Vector2(-50f, 0), body.getWorldCenter());
				}
				if (kiRec.keyMapping.containsKey("MOVE_RIGHT")) {
					KeyInputReceiver.KeyInputData kiData = kiRec.keyMapping.get("MOVE_RIGHT");
					if (kiData.pressed)
						body.applyLinearImpulse(new Vector2(50f, 0), body.getWorldCenter());
				}
			}
		}
		
		if (BOX_DEBUG_RENDERER)
			debugRenderer.render(physicsWorld, camera.combined);
	}
	
	@Override
	public void beginContact(Contact contact) {
		ArkanoidEntityFactory.getInstance().createCollisionEvent(CollisionEvent.CollisionType.BEGIN_CONTACT, contact);		
	}

	@Override
	public void endContact(Contact contact) {
		ArkanoidEntityFactory.getInstance().createCollisionEvent(CollisionEvent.CollisionType.END_CONTACT, contact);		
	}

	@Override
	public void preSolve(Contact contact, Manifold oldManifold) {
		ArkanoidEntityFactory.getInstance().createCollisionEvent(CollisionEvent.CollisionType.PRE_SOLVE, contact); // doterati
	}

	@Override
	public void postSolve(Contact contact, ContactImpulse impulse) {
		ArkanoidEntityFactory.getInstance().createCollisionEvent(CollisionEvent.CollisionType.POST_SOLVE, contact); // doterati
	}
}
